package com.mall.project.sys.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mall.common.core.asserts.ApiAssert;
import com.mall.common.core.enums.ErrorCodeEnum;
import com.mall.project.sys.cons.ScheduleCons;
import com.mall.project.sys.mapper.QuartzJobMapper;
import com.mall.project.sys.model.entity.QuartzJob;
import com.mall.project.sys.service.IQuartzJobService;
import com.mall.utils.CronUtils;
import com.mall.utils.ScheduleUtils;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.List;

/**
 * <p>
 * 定时任务调度表 服务实现类
 * </p>
 *
 * @author Sam
 * @since 2021-02-08
 */
@Slf4j
@Service
public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob> implements IQuartzJobService {

    @Autowired
    private Scheduler scheduler;

    /**
     * 项目启动时，初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理（注：不能手动修改数据库ID和任务组名，否则会导致脏数据）
     */
    @PostConstruct
    public void init() throws SchedulerException {
        scheduler.clear();
        List<QuartzJob> jobList = this.list();
        for (QuartzJob quartzJob : jobList) {
            ScheduleUtils.createScheduleJob(scheduler, quartzJob);
        }
        log.info("初始化 {} 个定时任务", jobList.size());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pauseJob(QuartzJob quartzJob) throws SchedulerException {
        Long jobId = quartzJob.getJobId();
        String jobGroup = quartzJob.getJobGroup();
        quartzJob.setStatus(ScheduleCons.Status.PAUSE);
        ApiAssert.isTrue(ErrorCodeEnum.BAD_UPDATE_FAILURE, this.updateById(quartzJob));
        scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resumeJob(QuartzJob quartzJob) throws SchedulerException {
        Long jobId = quartzJob.getJobId();
        String jobGroup = quartzJob.getJobGroup();
        quartzJob.setStatus(ScheduleCons.Status.NORMAL);
        ApiAssert.isTrue(ErrorCodeEnum.BAD_UPDATE_FAILURE, this.updateById(quartzJob));
        scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteJob(QuartzJob quartzJob) throws SchedulerException {
        Long jobId = quartzJob.getJobId();
        String jobGroup = quartzJob.getJobGroup();
        ApiAssert.isTrue(ErrorCodeEnum.BAD_DELETE_FAILURE, this.removeById(jobId));
        scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteJobByIds(Long[] jobIds) throws SchedulerException {
        for (Long jobId : jobIds) {
            QuartzJob quartzJob = this.getById(jobId);
            deleteJob(quartzJob);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeStatus(QuartzJob quartzJob) throws SchedulerException {
        Integer status = quartzJob.getStatus();
        if (ScheduleCons.Status.NORMAL == status) {
            resumeJob(quartzJob);
        } else if (ScheduleCons.Status.PAUSE == status) {
            pauseJob(quartzJob);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void run(Long jobId) throws SchedulerException {
        QuartzJob properties = this.getById(jobId);
        String jobGroup = properties.getJobGroup();
        // 参数
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleCons.TASK_PROPERTIES, properties);
        scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertJob(QuartzJob quartzJob) throws SchedulerException {
        quartzJob.setStatus(ScheduleCons.Status.PAUSE);
        ApiAssert.isTrue(ErrorCodeEnum.BAD_ADD_FAILURE, this.save(quartzJob));
        ScheduleUtils.createScheduleJob(scheduler, quartzJob);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateJob(QuartzJob quartzJob) throws SchedulerException {
        QuartzJob properties = this.getById(quartzJob.getJobId());
        ApiAssert.isTrue(ErrorCodeEnum.BAD_UPDATE_FAILURE, this.updateById(quartzJob));
        updateSchedulerJob(quartzJob, properties.getJobGroup());
    }

    public void updateSchedulerJob(QuartzJob quartzJob, String jobGroup) throws SchedulerException {
        Long jobId = quartzJob.getJobId();
        // 判断是否存在
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除，然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, quartzJob);
    }

    /**
     * 校验cron表达式是否有效
     *
     * @param cronExpression 表达式
     * @return 结果
     */
    @Override
    public boolean checkCronExpressionIsValid(String cronExpression) {
        return CronUtils.isValid(cronExpression);
    }
}
